#include "stm32f4xx_hal.h"
#include "at24cxx.h"
#include <string.h>

#ifdef EEPROM_DEBUG
  #include <stdio.h>
#endif

extern I2C_HandleTypeDef hi2c1;

#define EEPROM_PORT hi2c1

static int32_t E2WriteByte(uint16_t devaddr,uint16_t Addr, uint8_t Data)
{
    uint8_t buf[EEPROM_ADDR_SIZE + 1];
    uint16_t retry = 0xFFFF,len;
#if (EEPROM_TYPE <= 2)
    buf[0] = Addr;
    buf[1] = Data;
    len = 2;
#else
    buf[0] = Addr>>8;
    buf[1] = Addr;
    buf[2] = Data;
    len = 3;
#endif
    while(HAL_I2C_Master_Transmit(&EEPROM_PORT,devaddr,buf,len,I2C_TIMOUT)!= HAL_OK)
    {
        if (HAL_I2C_GetError(&EEPROM_PORT) != HAL_I2C_ERROR_AF)
        {
            return HAL_ERROR;
        }
        if(retry>1){
            retry--;
        }else{
            return HAL_ERROR;
        }
    }
    return HAL_OK;
}
static int32_t E2WritePage(uint16_t devaddr,uint16_t Addr,uint8_t *Data)
{
    uint8_t buf[EEPROM_ADDR_SIZE + EEPROM_PAGE_SIZE];
    uint16_t retry = 0xFFFF;
    if(Addr & (EEPROM_PAGE_SIZE-1)){
        return HAL_ERROR;
    }
#if (EEPROM_TYPE <= 2)
    buf[0] = Addr;
    memcpy(&buf[1],Data,EEPROM_PAGE_SIZE);
#else
    buf[0] = Addr>>8;
    buf[1] = Addr;
    memcpy(&buf[2],Data,EEPROM_PAGE_SIZE);
#endif
    while(HAL_I2C_Master_Transmit(&EEPROM_PORT,devaddr, buf, EEPROM_ADDR_SIZE + EEPROM_PAGE_SIZE,I2C_TIMOUT)!= HAL_OK)
    {
        if (HAL_I2C_GetError(&EEPROM_PORT) != HAL_I2C_ERROR_AF)
        {
            return HAL_ERROR;
        }
        if(retry>1){
            retry--;
        }else{
            return HAL_ERROR;
        }
    }
    return HAL_OK;
}
int E2Read(uint16_t devaddr,uint16_t Addr, uint8_t *Data, uint16_t len)
{
    uint8_t buf[EEPROM_ADDR_SIZE];
    uint16_t retry = 0xFF,wlen;
#if (EEPROM_TYPE <= 2)
    buf[0] = Addr;
    wlen = 1;
#else
    buf[0] = Addr>>8;
    buf[1] = Addr;
    wlen = 2;
#endif
    while(HAL_I2C_Master_Transmit(&EEPROM_PORT,devaddr,buf,wlen,I2C_TIMOUT)!= HAL_OK)
    {
        if (HAL_I2C_GetError(&EEPROM_PORT) != HAL_I2C_ERROR_AF)
        {
#ifdef EEPROM_DEBUG
            printf("HAL_I2C_Master_Transmit\r\n");
#endif
            return HAL_ERROR;
        }
        if(retry>1){
            retry--;
        }else{
            return HAL_ERROR;
        }
    }
    retry = 0xFF;
    while (HAL_I2C_GetState(&EEPROM_PORT) != HAL_I2C_STATE_READY)
    {
#ifdef EEPROM_DEBUG
        printf("HAL_I2C_GetState2 %x\r\n",HAL_I2C_GetState(&EEPROM_PORT));
#endif
        if(retry>1){
            retry--;
        }else{
            return HAL_ERROR;
        }
    }
    retry = 0xFF;
    while(HAL_I2C_Master_Receive(&EEPROM_PORT,devaddr,Data,len,I2C_TIMOUT) != HAL_OK)
    {
        if (HAL_I2C_GetError(&EEPROM_PORT) != HAL_I2C_ERROR_AF)
        {
#ifdef EEPROM_DEBUG
            printf("HAL_I2C_Master_Receive\r\n");
#endif
            return HAL_ERROR;
        }
        if(retry>1){
            retry--;
        }else{
            return HAL_ERROR;
        }
    }
    return HAL_OK;
}
int E2Write(uint16_t devaddr,uint16_t Addr,uint8_t *Data,uint16_t len)
{
  uint8_t buf[EEPROM_PAGE_SIZE];
  if(len==1){
    if(E2WriteByte(devaddr,Addr,*Data))
      return HAL_ERROR;
  }else{
    uint16_t secpos = Addr / EEPROM_PAGE_SIZE;
    uint16_t secoff = Addr % EEPROM_PAGE_SIZE;
    uint16_t secremain = EEPROM_PAGE_SIZE - secoff,i;
    if(len <= secremain)
      secremain = len;
    for(;;){
      if(secoff || (secremain != EEPROM_PAGE_SIZE)){
        if(E2Read(devaddr,secpos*EEPROM_PAGE_SIZE,buf,EEPROM_PAGE_SIZE))
          return HAL_ERROR;
      }
      for(i=0;i<secremain;i++)
      {
          buf[i+secoff] = Data[i];
      }
      if(E2WritePage(devaddr,secpos*EEPROM_PAGE_SIZE,buf))
        return HAL_ERROR;
      if(len==secremain){
#ifdef EEPROM_USING_HAL_DELAY
      HAL_Delay(1);
#endif
        break;
      }else{
        secpos++;
        secoff = 0;
        Data += secremain;
        Addr += secremain;
        len -= secremain;
        if(len>EEPROM_PAGE_SIZE)
          secremain=EEPROM_PAGE_SIZE;
        else
          secremain=len;
      }
#ifdef EEPROM_USING_HAL_DELAY
      HAL_Delay(1);
#endif
    }
  }
    return HAL_OK;
}

